package com.inspur.keycloak.authenticator;

import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
import org.keycloak.models.*;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Response;
import java.io.ByteArrayOutputStream;
import java.util.Base64;


public class CaptchaAuthenticator extends UsernamePasswordForm {

	private Logger logger = LoggerFactory.getLogger(CaptchaAuthenticator.class);

	@Override
	public void authenticate(AuthenticationFlowContext context) {
		AuthenticatorConfigModel config = context.getAuthenticatorConfig();
		int ttl = Integer.parseInt(config.getConfig().get("ttl"));
		int length = Integer.parseInt(config.getConfig().get("length"));

		String verifyCode = VerifyCodeUtils.generateVerifyCode(length);

		logger.info("生成验证码:{}", verifyCode);

		AuthenticationSessionModel authSession = context.getAuthenticationSession();
		authSession.setAuthNote("verifyCode", verifyCode);
		authSession.setAuthNote("ttl", Long.toString(System.currentTimeMillis() + (ttl * 1000L)));
		// 生成图片
		try (ByteArrayOutputStream stream = new ByteArrayOutputStream();) {
			int w = 111, h = 36;
			VerifyCodeUtils.outputImage(w, h, stream, verifyCode);

			String captchaImg = Base64.getEncoder().encodeToString(stream.toByteArray());
			context.form().setAttribute("verifyCodeImg", "data:image/png;base64," + captchaImg);
		} catch (Exception e) {
			logger.error("验证码生成异常", e);
		}

		super.authenticate(context);

	}

	@Override
	public void action(AuthenticationFlowContext context) {
		AuthenticatorConfigModel config = context.getAuthenticatorConfig();
		String case_sensitive = config.getConfig().get("case_sensitive");

		// 从请求表单参数中读取验证码
		String enteredCode = context.getHttpRequest().getDecodedFormParameters().getFirst("verifyCode");

		AuthenticationSessionModel authSession = context.getAuthenticationSession();
		String verifyCode = authSession.getAuthNote("verifyCode");
		String ttl = authSession.getAuthNote("ttl");

		if (verifyCode == null || ttl == null) {
			logger.error("验证码和超时时间为空");
			context.failure(AuthenticationFlowError.INTERNAL_ERROR);
			return;
		}

		logger.info("待校验验证码:{},输入验证码:{}", verifyCode, enteredCode);
		boolean isValid;
		if ("true".equalsIgnoreCase(case_sensitive)) {
			isValid = verifyCode.equals(enteredCode);
		} else {
			isValid = verifyCode.equalsIgnoreCase(enteredCode);

		}

		if (!isValid) {
			AuthenticationExecutionModel execution = context.getExecution();
			if (execution.isRequired()) {
				Response response = context.form().setError("smsAuthCodeInvalid").createLoginUsernamePassword();
				context.failure(AuthenticationFlowError.ACCESS_DENIED, response);
				context.resetFlow();
			} else if (execution.isConditional() || execution.isAlternative()) {
				context.attempted();
			}
			return;

		}

		if (Long.parseLong(ttl) < System.currentTimeMillis()) {
			Response response = context.form().setError("smsAuthCodeExpired").createLoginUsernamePassword();
			context.failure(AuthenticationFlowError.ACCESS_DENIED, response);
			context.resetFlow();
		} else {
			super.action(context);
			AuthenticationFlowError error = context.getError();
			if (error != null) {
				context.resetFlow();
			}
		}


	}

	@Override
	public boolean requiresUser() {
		return false;
	}

	@Override
	public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
		return true;
	}

	@Override
	public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
	}

	@Override
	public void close() {
	}
}
